//AKAOKA Hicham
//BEN JEMIA Mohamed El Fadhel
package in.ece.ing4.si.mtmultithread;

import in.ece.ing4.si.mtsequential.Option;

import java.text.DecimalFormat;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Random;
import java.io.IOException;
import java.util.concurrent.ExecutionException;

public class OptionMulti {
	private double strikePrice; 
	private String callPutFlag;
	private double maturity;
	private double currentPrice;
	private double discountRate;
	private double costCarry;
	private double volatility;
	private int nbStep; 
	private int nbSimu;
	
	public OptionMulti(String callPutFlag, double strikePrice, double maturity, double currentPrice, double discountRate, double costCarry, double volatility, int nbStep, int nbSimu)
	{
		this.callPutFlag=callPutFlag;
		this.strikePrice=strikePrice;
		this.maturity=maturity;
		this.currentPrice=currentPrice;
		this.discountRate=discountRate;
		this.costCarry=costCarry;
		this.volatility=volatility;
		this.nbStep=nbStep; 
		this.nbSimu=nbSimu;
	}
	
	public double monteCarloStandardOption(String callPutFlag, double currPrice, double strPrice, double tillMaturity, double discountRate, double costCarry, double volatil, int nbStep, int nbSimu) {
		double dt, st, sum = 0, drift, vSqrt; 
		int i, j, flg = 0;
		
		dt=tillMaturity/nbStep;
		drift=(costCarry-volatil*volatil/2)*dt;
		vSqrt=volatil*(java.lang.Math.sqrt(dt));
				
		if(callPutFlag.equals("c"))
		{
			flg=1;
		}
		else if(callPutFlag.equals("p"))
		{
			flg=-1; 
		}
		
		Random random=new Random(); 
		
		for(i=1;i<=nbSimu;i++)
		{
			st=currPrice;
			
			for(j=1;j<=nbStep;j++)
				st=st*(java.lang.Math.exp(drift+vSqrt*random.nextGaussian())); 
			
			sum=sum+(java.lang.Math.max(flg*(st-strPrice),0));
		}
		
		double estimPrice=(java.lang.Math.exp((-discountRate*tillMaturity)*(sum/nbSimu))); 

		return estimPrice; 
	}
	
	public double compute() throws InterruptedException, ExecutionException {
		int nbCores=Runtime.getRuntime().availableProcessors();
	    double blockingCoef=1;
	    int poolSize=(int)(nbCores/(1-blockingCoef));
	    
	    System.out.println("The number of available cores is: " + nbCores);
	    System.out.println("The pool size is: " + poolSize);
	    final List<Callable<Double>> partitions=new ArrayList<Callable<Double>>();
	    
	    final ExecutorService executorPool=Executors.newFixedThreadPool(poolSize);    
	    final List<Future<Double>> valueOfStocks=executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
	      
	    double netAssetValue=0.0;
	    for(final Future<Double> valueOfAStock : valueOfStocks)  
	      netAssetValue += valueOfAStock.get(); 

	    executorPool.shutdown();
	    return netAssetValue;   
	  }
	
	 public void time() throws ExecutionException, InterruptedException, IOException {
		 final long start = System.nanoTime(); //--get time of start
		 final long end = System.nanoTime(); //--get time of stop
		 System.out.println("Simulation completed in " + (end - start)/1.0e9 + " seconds");
		 }
	/**
	 * @param args
	 * @throws IOException 
	 * @throws InterruptedException 
	 * @throws ExecutionException 
	 */
	public static void main(String[] args) throws ExecutionException, InterruptedException, IOException {
		// TODO Auto-generated method stub
		final long start=System.nanoTime(); //--get time of start
		OptionMulti opt=new OptionMulti("c", 50, 0.5, 40, 0.06, 0.10, 0.45, 168, 100000);
		double i=opt.monteCarloStandardOption(opt.callPutFlag, opt.currentPrice, opt.strikePrice, opt.maturity,opt.discountRate,opt.costCarry,opt.volatility,opt.nbStep,opt.nbStep);
		System.out.println("The estimated price is "+i);
		final long end=System.nanoTime(); //--get time of stop
		System.out.println("Simulation completed in " + (end - start)/1.0e9 + " seconds.");
		opt.compute();
	}

}
